use crate::manifest::{ContributeDefinition, ContributionPointDefinition};
use crate::services::event_manager::{DynamicContributesAddedEvent, EventManager, ProcessContributionsEvent};
use parking_lot::RwLock;
use std::collections::HashMap;
use std::sync::Arc;

pub trait IContributionKeywordImpl: Send + Sync {
    fn request_contribution(&mut self, ctx: RequestContributionContext, definitions: Vec<ContributeDefinition>);
    fn request_contribution_point(
        &mut self,
        ctx: RequestContributionContext,
        definitions: Vec<ContributionPointDefinition>,
    );
}

pub struct RequestContributionContext {
    pub contributor: String,
}

pub trait IContributionManager {
    fn implement_contribution_keyword(&self, name: &str, implementation: Box<dyn IContributionKeywordImpl>);
    fn subscribe_to_events(&self, event_manager: &EventManager);
}

#[derive(Clone)]
pub struct ContributionKeywordManager {
    implementations: Arc<RwLock<HashMap<String, Box<dyn IContributionKeywordImpl>>>>,
}

impl ContributionKeywordManager {
    pub fn new() -> Self {
        Self { implementations: Arc::new(RwLock::new(HashMap::new())) }
    }
}

impl IContributionManager for ContributionKeywordManager {
    fn implement_contribution_keyword(&self, name: &str, implementation: Box<dyn IContributionKeywordImpl>) {
        self.implementations.write().insert(name.to_string(), implementation);
    }

    fn subscribe_to_events(&self, event_manager: &EventManager) {
        let implementations = self.implementations.clone();
        event_manager.on(move |event: &ProcessContributionsEvent| {
            let mut impls = implementations.write();
            for (keyword, definitions) in &event.contributes {
                if let Some(keyword_impl) = impls.get_mut(keyword) {
                    let ctx = RequestContributionContext { contributor: event.contributor_name.clone() };
                    keyword_impl.request_contribution(ctx, definitions.clone());
                }
            }
            for (keyword, definitions) in &event.contribution_points {
                if let Some(keyword_impl) = impls.get_mut(keyword) {
                    let ctx = RequestContributionContext { contributor: event.contributor_name.clone() };
                    keyword_impl.request_contribution_point(ctx, definitions.clone());
                }
            }
        });

        let implementations_clone = self.implementations.clone();
        event_manager.on(move |event: &DynamicContributesAddedEvent| {
            let mut impls = implementations_clone.write();
            if let Some(keyword_impl) = impls.get_mut(&event.keyword) {
                let ctx = RequestContributionContext { contributor: event.contributor_name.clone() };
                keyword_impl.request_contribution(ctx, event.definitions.clone());
            }
        });
    }
}